﻿using RemoteSensingImageDirectServer.Models.Request.Listener;
using RemoteSensingImageDirectServer.Models.Request.Response;
using RemoteSensingImageDirectServer.Untils.Extensions;
using RemoteSensingImageDirectServer.Untils.ScopeSpace;
using RemoteSensingImageDirectServer.Untils.Source;
using Microsoft.AspNetCore.Mvc;
using OpenCvSharp;
using System.Drawing;

namespace RemoteSensingImageDirectServer.Service.OGC.RasterMap
{
    public class RasterSourceTile : IRasterSourceTile
    {
        /// <summary>
        /// 从数据源获取瓦片
        /// </summary>
        /// <param name="source">数据源</param>
        /// <param name="imageListener">请求参数</param>
        /// <param name="tileEnvelope">请求范围</param>
        /// <param name="imageXSize">瓦片宽度</param>
        /// <param name="imageYSize">瓦片高度</param>
        /// <param name="resultCallback">结果传递回调结构体</param>
        /// <returns>服务结果</returns>
        public ServiceResult GetRasterTile(IRSIMapSource source, IImageListener imageListener, 
            RasterEnvelope tileEnvelope, 
            int imageXSize, int imageYSize,
            ServiceResult? resultCallback = null)
        {
            ServiceResult serviceResult = resultCallback ?? new ServiceResult(0, "");

            var dstDataset = source.GetDstDataset();
            var dstEnvelope = source.GetDstRasterEnvelope();
            var isMask = imageListener.IsMask;
            var bgColor = imageListener.BGColor;
            var bandArr = imageListener.Bands;
            int bandCount = bandArr.Length;

            IStretchListener stretchListener = (IStretchListener)imageListener;
            var percentLeft = stretchListener.PercentLeft;
            var percentRight = stretchListener.PercentRight;
            var gamma = stretchListener.Gamma;

            var intersetEnvelope = dstEnvelope.Insersetect(tileEnvelope);
            if (intersetEnvelope.MinX >= intersetEnvelope.MaxX || intersetEnvelope.MaxY <= intersetEnvelope.MinY)
            {
                serviceResult.Code = -1;
                serviceResult.Message = "";
                return serviceResult;
            }

            var readTileRange = dstEnvelope.CalculateTileRange(intersetEnvelope);
            var readWidth = readTileRange.EndCol - readTileRange.StartCol;
            var readHeight = readTileRange.EndRow - readTileRange.StartRow;

            var writeTileRange = tileEnvelope.CalculateTileRange(intersetEnvelope);
            var writeWidth = writeTileRange.EndCol - writeTileRange.StartCol;
            var writeHeight = writeTileRange.EndRow - writeTileRange.StartRow;

            if (readWidth.Equals(0) || readHeight.Equals(0) || writeWidth.Equals(0) || writeHeight.Equals(0))
            {
                serviceResult.Code = -1;
                serviceResult.Message = "";
                return serviceResult;
            }

            Mat? tileMat = null;
            Mat[]? mats = null;
            if (bandCount == 1)
            {
                mats = new Mat[bandCount];
            }
            else if (bandCount == 3)
            {
                mats = new Mat[bandCount + 1];
                mats[bandCount] = new Mat(imageYSize, imageXSize, MatType.CV_8UC1, new Scalar(255));//toMask || HistogramMin == 0 ? 255 : 0
            }
            else
            {
                serviceResult.Code = -1;
                serviceResult.Message = "";
                return serviceResult;
            }

            var backColor = ColorTranslator.FromHtml(bgColor);
            var backColorArray = new byte[] { backColor.B, backColor.G, backColor.R };//获取背景色

            try
            {
                for (int i = 0; i < bandCount; i++)
                {
                    int bandIndex = bandArr[i];
                    var band = dstDataset.GetRasterBand(bandIndex);
                    band.GetNoDataValue(out double nodata, out int hasval);

                    if (band.DataType == OSGeo.GDAL.DataType.GDT_Byte)
                    {
                        var imageArr = new byte[writeWidth * writeHeight];
                        band.ReadRaster(readTileRange.StartCol, readTileRange.StartRow, readWidth, readHeight, imageArr, writeWidth, writeHeight, 0, 0);

                        mats[i] = imageArr.FillArray(writeTileRange, imageXSize, imageYSize, 0, 255, nodata, backColorArray[i]);
                    }
                    else if (band.DataType == OSGeo.GDAL.DataType.GDT_Int16)
                    {
                        var imageArr = new short[writeWidth * writeHeight];
                        band.ReadRaster(readTileRange.StartCol, readTileRange.StartRow, readWidth, readHeight, imageArr, writeWidth, writeHeight, 0, 0);

                        mats[i] = imageArr.FillArray(writeTileRange, imageXSize, imageYSize, 0, 255, nodata, backColorArray[i]);
                    }
                    else if (band.DataType == OSGeo.GDAL.DataType.GDT_Float32)
                    {
                        var imageArr = new float[writeWidth * writeHeight];
                        band.ReadRaster(readTileRange.StartCol, readTileRange.StartRow, readWidth, readHeight, imageArr, writeWidth, writeHeight, 0, 0);

                        mats[i] = imageArr.FillArray(writeTileRange, imageXSize, imageYSize, backColorArray, 0, 255, nodata);
                    }
                    else if (band.DataType == OSGeo.GDAL.DataType.GDT_Float64)
                    {
                        var imageArr = new double[writeWidth * writeHeight];
                        band.ReadRaster(readTileRange.StartCol, readTileRange.StartRow, readWidth, readHeight, imageArr, writeWidth, writeHeight, 0, 0);

                        mats[i] = imageArr.FillArray(writeTileRange, imageXSize, imageYSize, backColorArray, 0, 255, nodata);
                    }
                    else
                    {
                        var imageArr = new int[writeWidth * writeHeight];
                        band.ReadRaster(readTileRange.StartCol, readTileRange.StartRow, readWidth, readHeight, imageArr, writeWidth, writeHeight, 0, 0);

                        mats[i] = imageArr.FillArray(writeTileRange, imageXSize, imageYSize, 0, 255, nodata, backColorArray[i]);
                    }
                }
            }
            catch (Exception ms)
            {
                serviceResult.Code = -1;
                serviceResult.Message = ms.Message;
                return serviceResult;
            }

            tileMat = mats[0];

            if (mats.Count() > 1) Cv2.Merge(mats, tileMat);

            tileMat = tileMat.Mask(source, tileEnvelope, isMask, imageXSize, imageYSize, bandCount);

            tileMat = tileMat.Gamma(gamma);

            var tileStream = tileMat.ToStream(imageListener, out string imageFormat);

            serviceResult.Data = new FileContentResult(tileStream, imageFormat);

            return serviceResult;
        }
    }
}
